// Copyright (c) 2010, Anthony Cassidy
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, are 
// permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice, this list 
//         of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this 
//         list of conditions and the following disclaimer in the documentation and/or 
//         other materials provided with the distribution.
//     * Neither the name of the AntsAdventureGameCreator nor the names of its contributors 
//         may be used to endorse or promote products derived from this software without 
//         specific prior written permission.
// 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
#include "XStdafx.h"
#include "XChoicesDialog.h"
#include "Choices.h"
#include <QTableWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QSignalMapper>
#include "QSettings.h"

static const int CELLHEIGHT =20;
XChoicesDialog::XChoicesDialog(std::auto_ptr<Choices>& ch, QWidget* parent)
: QDialog(parent)
, m_table(* new QTableWidget(this))// del by parent
, m_choices( *ch.get() )
{
	QPushButton* button = new QPushButton("(* add choice)", this);// del by parent
	bool res = connect(button,SIGNAL(clicked()), this, SLOT(OnAddChoice()));
	bool res2 = connect(this, SIGNAL(accepted()), this, SLOT(OnAccepted()));
	bool res3 = connect(this, SIGNAL(rejected()), this, SLOT(OnRejected()));

	ASSERT(res && "connect failed");  
	ASSERT(res2);
	ASSERT(res3);

	this->setLayout(new QVBoxLayout(this));// del by parent
	this->layout()->addWidget(button);
	this->layout()->addWidget(&m_table);



	Update();
}


QString XChoicesDialog::ConstructKeyForParticularChoice(Choices ch, int i)
{
	QString dialogName = ch.m_name;
	QString chosenText = ch.m_displayStrings[i];
	QString key = dialogName+"/"+chosenText;
	return key;
}

void XChoicesDialog::SetIsParticularChoiceChosen(Choices ch, int i)
{
	QSettings settings("MySoft", "Star Runner");
	settings.setValue(ConstructKeyForParticularChoice(ch,i), true);
}

bool XChoicesDialog::IsParticularChoiceChosen(Choices ch, int i)
{
	QSettings settings("MySoft", "Star Runner");
	bool isSetCorrectly = settings.value(ConstructKeyForParticularChoice(ch,i)).toBool();
	return isSetCorrectly;
}

void XChoicesDialog::Update()
{
	m_table.clear();
	m_table.disconnect(this);
	this->disconnect(&m_table);
	m_table.setColumnCount(4);
	m_table.setRowCount( m_choices.m_displayStrings.size());
	QStringList horizontalLabels;
	horizontalLabels.push_back("");
	horizontalLabels.push_back("User sees this");
	horizontalLabels.push_back("Source code");
	horizontalLabels.push_back("Comment to add to code");
	m_table.setHorizontalHeaderLabels(horizontalLabels);
	
	QString longestChoice = horizontalLabels[1];
	QString longestFile = horizontalLabels[3];
	QString longestComment = horizontalLabels[3];

	// another object whose can work entirely behind the scenes
	QSignalMapper* buttonSignalMapper = new QSignalMapper(this);// del by parent
	bool isConnected = connect(buttonSignalMapper, SIGNAL(mapped(int)),this, SLOT(done(int)));
	ASSERT(isConnected && "connect failed");

	int numberOfStrings = m_choices.m_displayStrings.size();
	for(int i=0;i<numberOfStrings;i++)
	{
		QPushButton* button = new QPushButton("ok", this);// del by parent
		button->resize(20,20);
		buttonSignalMapper->setMapping(button, i+2);
		bool isConnected = connect(button, SIGNAL(clicked()),buttonSignalMapper, SLOT (map()));	
		ASSERT(isConnected && "connect failed");
		m_table.setCellWidget(i, 0, button);

		bool isVisible = m_choices.m_isVisible[i];
		bool isOneShot = m_choices.m_isOneShot[i];
		if(isOneShot)
		{
			if(IsParticularChoiceChosen(m_choices,i))
			{
				isVisible = false;
			}
		}

		QString text = m_choices.m_displayStrings[i] + " " + (!isVisible? "(hidden)" : "");
		if(text.length() > longestChoice.length())
			longestChoice = text;
		QTableWidgetItem* itemForChoice = new QTableWidgetItem(text);
		itemForChoice->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable);
		m_table.setItem(i,1,itemForChoice);

		QString fileString = QString("%1 (%2)").arg(m_choices.m_files[i]).arg(m_choices.m_lines[i]);
		QTableWidgetItem* file = new QTableWidgetItem(fileString);
		if(fileString.length() > longestFile.length())
			longestFile = fileString;
		file->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
		m_table.setItem(i,2,file);

		QTableWidgetItem* comment = new QTableWidgetItem(m_choices.m_comments[i]);
		if(m_choices.m_comments[i].length() > longestChoice.length())
			longestComment = m_choices.m_comments[i];
		comment->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable);
		m_table.setItem(i,3,comment);

	}
	
	QFont font;//("arial",10);
	QFontMetrics fm(font);
	int padding = 10;
	int longestChoiceWidth = fm.width(longestChoice) + padding;
	int longestCommentWidth = fm.width(longestComment) + padding;
	int longestFileWidth = fm.width(longestFile) + padding;
	int firstColumnWidth = fm.width(" ") + padding;
	int rowCount = m_table.rowCount();
	for(int i=0;i<rowCount;i++)
	{
		m_table.setColumnWidth(0,20);
		m_table.setColumnWidth(1,longestChoiceWidth);
		m_table.setColumnWidth(2,longestFileWidth);
		m_table.setColumnWidth(3,longestCommentWidth);
	}

	int rowHeight = m_table.rowHeight(1);//if this crashes, change to index zero
	int headerHeight = m_table.rowHeight(0);
	int tableWidth = longestChoiceWidth + longestCommentWidth + longestFileWidth + firstColumnWidth + 50;//50 is first
	int tableHeight = m_choices.m_displayStrings.size()*rowHeight + headerHeight;
	
	m_table.resize(tableWidth, tableHeight+30);
	this->resize(tableWidth, tableHeight+50);// 30 is height of "add item" button

	// only reconnect after all cells have been replaced
	int a = connect(&m_table,SIGNAL(cellPressed(int,int)),this, SLOT(OnCellPressed(int,int)));
	int b = connect(&m_table,SIGNAL(cellChanged(int,int)),this, SLOT(OnCellChanged(int,int)));

	ASSERT(a && "failed to connect");
	ASSERT(b && "failed to connect");
}


void XChoicesDialog::OnCellPressed(int row, int column)
{
	if(column==0)
	{
		if(row<m_choices.m_displayStrings.size())
		{
			QDialog::done(row+2);
		}
	}
}
bool XChoicesDialog::TryReadLine(QString filename, int lineNumber, QString* toReturn)
{
	if(filename.length() ==0)
		return false;
	if(lineNumber<0)
		return false;

	QFile input(filename);
	if (!input.open(QFile::ReadOnly))
		return false;
	
	bool isReturnValueValid = false;

	for(int i=0;;i++)
	{
		const int sizeOfBuffer = 3000;
		std::vector<char> bytes(sizeOfBuffer);	
		qint64 charsRead = input.readLine(&bytes[0],sizeOfBuffer-1);

		if(charsRead<2)
			break;
			
		QString line = QString::fromUtf8(&bytes[0]);
		if(lineNumber == i)
		{
			*toReturn = line;
			isReturnValueValid = true;
			break;
		}
	}
	input.close();
	return isReturnValueValid;
}

void AddCommentOrReplaceString(QString filename, int lineNumber, QString newText, QString oldText="")
{
	QFile input(filename);
	QFile output(filename+".out");
	 
	if (!input.open(QFile::ReadOnly))
		return;
	if (!output.open(QFile::WriteOnly))
		return;

	for(int i=0;;i++)
	{
		const int sizeOfBuffer = 3000;
		std::vector<char> bytes(sizeOfBuffer);	
		qint64 charsRead = input.readLine(&bytes[0],sizeOfBuffer-1);

		if(charsRead<2)
			break;
			
		QString line = QString::fromUtf8(&bytes[0]);

		// the replace displayString case
		if(oldText.length())
		{
			// replace on both lines just incase its a two line command
			if(lineNumber == i || lineNumber == i-1)
			{
				line.replace(newText,oldText,Qt::CaseInsensitive);
			}
		}
		// the replace comment case
		else if (lineNumber ==i)
		{
			//remove existing comment
			if(line.contains("//"))
			{
				line.chop(line.lastIndexOf("//"));
			}
			// add a fresh comment
			line.append("// ");
			line.append(newText);
		}
		output.write(line.toUtf8());
	}

	input.close();
	output.close();

		 
	if (!output.open(QFile::ReadOnly))
		return;
	if (!input.open(QFile::WriteOnly))
		return;
	
	QByteArray all = output.readAll();
	input.write(all);

	output.close();
	input.close();
}

void ReplaceQuote(QString file, int line, QString newText, QString oldText)
{
	AddCommentOrReplaceString(file,line,newText,oldText);
}

void AddComment(QString file, int line, QString comment)
{
	AddCommentOrReplaceString(file,line,comment);
}

void XChoicesDialog::OnCellChanged(int row, int column)
{
	QString newText = m_table.item(row, column)->text();
	QString file = m_choices.m_displayStrings[row];
	int line = m_choices.m_lines[row];
	QString displayText = m_choices.m_displayStrings[row];
	QString comment = m_choices.m_comments[row];
	if(column==1)// edit the quote
	{
		ReplaceQuote(file, line, displayText, newText);
		m_choices.m_displayStrings[row] = newText;
	}
	else if(column==3)// edit the comment
	{
		AddComment(file,line,comment);
		m_choices.m_comments[row] = comment;
	}
}

void XChoicesDialog::OnAddChoice()
{
	int nextNewIndex = 100; // todo
	m_choices.m_helper.Add(nextNewIndex,"","",-1,true);
	Update();
}

void XChoicesDialog::OnAccepted()
{
	// do nothing, just here for debugging
}

void XChoicesDialog::OnRejected()
{
	// this essentially forces a press of [X] from a rejected to accepted
	done(1);
}



int XChoicesDialog::Exec()
{
	// ading layer of abstraction incase I need it;
	int i = QDialog::exec();

	if(i>=2)
	{
		SetIsParticularChoiceChosen(m_choices, i-2);
	}

	return i;
}

